#!/usr/bin/env bash

include "libs/ohos.sh"
include "libs/test.sh"
include "libs/output.sh"
include "libs/regex.sh"

#help.cmd cc@blame BLAME_TARGET : (仓库) 查看编译目标的详情
#alias.cmd ocbl : cc@blame
dynamic_cmd:cc:blame()
{
    ohos:cd_repo || return
    BLAME_TARGET="$1"
    ohos:check_param BLAME_TARGET || return
    filename="$(pwd)/$(sed 's@[^:]*:\([^(]*\)\((.*)\)\?@\1@g' <<< "$BLAME_TARGET").blame.gn"
    docker:exec gn desc --blame $OHOS_OUT_DIR --all --tree "\"$BLAME_TARGET\"" > $filename
    cd $COMMAND_CURDIR
    output:logi "输出文件在$(print:pathfile:get_pathfile $filename)"
}

# dynamic.option --product PRODUCT : 指定编译平台，默认为Hi3516DV300
# --product.option.list: cc@hosp cc@alltest cc@buildgn cc@rebuild
# --product.option.list: utils@stack hdc@autopush
dynamic_option:--product()
{
    PRODUCT="$(arguments:get_now)"
    ohos:check_param PRODUCT || return
    arguments:next
    return 0
}

COUNT_ENABLE=false
# dynamic.option --count : 只打印错误个数，不打印内容
# --count.option.list: cc@err
dynamic_option:--count()
{
    COUNT_ENABLE=true
}

#help.cmd cc@err [id[, id]*] : (仓库) 打印编译的错误日志，可以选择打印某一项
#alias.cmd oce : cc@err
#alias.cmd ocec : cc@err --count
dynamic_cmd:cc:err()
{
    if ! ohos:buildlog_init; then
        echo -e "$(cctl y)识别不到错误$(cctl 0)"
        return 1
    fi

    if $COUNT_ENABLE; then
        output:logi "错误有$(ohos:buildlog_count)个"
        return 1
    fi

    local ids=()
    local try_add_id=false

    arguments $*
    while arguments:have_next; do
        try_add_id=true
        local id="$(arguments:get_now)"
        if ! [[ "$id" =~ (${regex_dec_integer}) ]]; then
            output:logw "${id}不是十进制数字"
        elif ! (( "1" <= "$id" )); then
            output:logw "${id}小于1，是非法数字"
        elif ! (( "$id" <= "$(ohos:buildlog_count)" )); then
            output:logw "${id}大于错误的个数，是非法数字"
        else
            ids+=("$((id - 1))")
        fi

        arguments:next
    done
    arguments:destroy

    if $try_add_id && [ "${#ids[*]}" == "0" ]; then
        output:logi "没有可以打印的项"
        return 1
    fi

    if ! $try_add_id; then
        for (( i = 0; i < $(ohos:buildlog_count); i++ )); do
            ids+=($i)
        done
    fi

    echo -e "\n$(cctl r)错误分析:$(cctl 0)\n"
    for i in ${ids[*]}; do
        local result="$(ohos:buildlog_display $i)"
        if [[ "$result" =~ ^(FAILED: ) ]]; then
            if [[ "$result" =~ (^FAILED: [_/a-zA-Z0-9-]*obj/[a-zA-Z0-9_/.-]*\.o) ]]; then
                # clang/clang++
                local left="\(^.*:[0-9]\+:[0-9]\+: \)\(fatal error\|error\|note\)\(.*\)"
                local right="$(cctl c)\1$(cctl r)\2$(cctl y)\3$(cctl 0)"
                result="$(sed "s@$left@$right@g" <<< "$result")"

                result="$(sed "s@.*prebuilts[^ ]*/clang.*@$(cctl g)\0$(cctl 0)@g" <<< "$result")"
                result="$(sed "s@\(note\)@$(cctl p)\1$(cctl 0)@g" <<< "$result")"
            elif [[  "$result" =~ (^FAILED: [a-zA-Z0-9_/.-]*(\.so)?\n) ]]; then
                # ld
                result="$(sed "s@\(^ld\.lld.*\)@$(cctl r)\1$(cctl 0)@g" <<< "$result")"
                result="$(sed "s@.*prebuilts/clang.*@$(cctl g)\0$(cctl 0)@g" <<< "$result")"
                result="$(sed "s@\(undefined symbol: \)\(.*\)@$(cctl g)\1$(cctl c)\2$(cctl 0)@g" <<< "$result")"
            elif [[ "$result" =~ (Traceback) ]]; then
                # python
                result="$(sed "s@^/usr/bin/env.*@$(cctl g)\0$(cctl 0)@g" <<< "$result")"
                result="$(sed "s@\(File \".*\", \)\(line [0-9]\+, \)\(in .*\)@$(cctl c)\1$(cctl r)\2$(cctl y)\3$(cctl 0)@g" <<< "$result")"
                result="$(sed "s@    .*@$(cctl p)\0$(cctl 0)@g" <<< "$result")"
                result="$(sed "s@Traceback@$(cctl y)\0$(cctl 0)@g" <<< "$result")"
            else
                # other
                result="${result}\n没有上色方案"
            fi
        elif [[ "$result" =~ (^ERROR ) ]]; then
            local gn_target="//[a-zA-Z0-9_./-]*\(:[a-zA-Z_]\w*\)\?"
            if [[ "$result" =~ (^ERROR Unresolved dependencies.) ]]; then
                result="$(sed "s@\(ERROR\)\(.*\)@$(cctl r)\1$(cctl y)\2$(cctl 0)@g" <<< "$result")"
                result="$(sed "s@^$gn_target@$(cctl p)\0$(cctl 0)@g" <<< "$result")"
                result="$(sed "s@\(  needs \)\($gn_target\)@$(cctl c)\1$(cctl g)\2$(cctl 0)@g" <<< "$result")"
            elif [[ "$result" =~ (Unable to load) ]]; then
                result="$(head -3 <<< "$result")"
                result="$(sed "s@\(ERROR\)\(.*\)@$(cctl r)\1$(cctl y)\2$(cctl 0)@g" <<< "$result")"
                result="$(sed "s@$gn_target@$(cctl c)\0$(cctl 0)@g" <<< "$result")"
            else
                result="$(sed "s@\(ERROR\)\(.*\)@$(cctl r)\1$(cctl y)\2$(cctl 0)@g" <<< "$result")"
                result="$(sed "s@$gn_target@$(cctl c)\0$(cctl 0)@g" <<< "$result")"
            fi
        fi
        result="$(sed "s@^@$(cctl bg444444)* | $(cctl 0)@g" <<< "$result")"
        output:logi "Error($((i + 1))/$(ohos:buildlog_count)):\n$result\n"
    done
    return 1
}

# function dynamic_cmd:cc:err:test()
# {
#     REPO_PATH="$(pwd)"
#     CONTAINER_NAME="test"
#     test_build_log=$OHOS_TOOL/snapshot/build_log_0
#     dynamic_cmd:cc:err
# }
# test:register_test dynamic_cmd:cc:err:test

# cc@hosp.option --test TEST : 增加测试模块TEST
dynamic_cmd:cc:hosp--test()
{
    TEST="$(arguments:get_now)"
    ohos:check_param TEST || return
    arguments:next

    ohos:cd_repo || return
    local buildgns="$(find foundation/ device/ drivers/ -name BUILD.gn)"
    for buildgn in $(grep "^module_out_path = \".*$TEST\"" -l $buildgns); do
        for test in $(cat $buildgn | grep "^ohos_unittest" | cut -d '"' -f 2); do
            [ "$test" != "" ] || continue
            DOCKER_ARGS+=(--build-target $test)
        done
    done
    cd - >/dev/null
    return 0
}

# cc@hosp.option --target TARGET : 增加编译目标TARGET
dynamic_cmd:cc:hosp--target()
{
    TARGET="$(arguments:get_now)"
    ohos:check_param TARGET || return
    arguments:next
    [ "$TARGET" != "-*" ] || report "$OPT需要一个参数，但'$TARGET'不是参数" || return
    DOCKER_ARGS+=(--build-target $TARGET)
    return 0
}

# cc@hosp.option --nostop : 编译出错不立刻停止编译，绕过出错模块
dynamic_cmd:cc:hosp--nostop()
{
    OHOS_NINJA_ERROR_NUM=99999
    return 0
}

# cc@hosp.option --rmout : 编译时删除out，默认不删除
dynamic_cmd:cc:hosp--rmout()
{
    OHOS_REMOVE_OUT=1
    return 0
}

#help.cmd cc@hosp : (仓库) 整编，出镜像
#alias.cmd och : cc@hosp
dynamic_cmd:cc:hosp()
{
    if [ "$OHOS_REMOVE_OUT" = "1" ]; then
        ohos:cd_repo || return
        rm -rf out
    fi

    ohos:cd_repo || return
    rm -f kernel/linux-4.19/.git/packed-refs
    rm -f kernel/linux-4.19/.git/shallow
    rm -f $REPO_PATH/${OHOS_OUT_DIR}/build.log
    pushd $REPO_PATH/drivers/framework/tools/hc-gen 1>/dev/null
        [ -e Makefile ] && make clean
    popd 1>/dev/null

    DOCKER_ARGS+=(--product-name $PRODUCT)

    cd $REPO_PATH/build
    if grep "\--keep-ninja-going" scripts/entry.py >/dev/null 2>&1; then
        DOCKER_ARGS+=(--keep-ninja-going)
    else
        DOCKER_ARGS+=(--ninja-args -k${OHOS_NINJA_ERROR_NUM})
    fi
    cd - >/dev/null

    docker:exec ./build.sh ${DOCKER_ARGS[@]}
    if ! message_box; then
        dynamic_cmd:cc:err
        return 1
    fi
    docker:exec rm -rf "/tmp/tmp*"
}

#help.cmd cc@buildgn : (仓库) 编译BUILD.gn
#alias.cmd ocbg : cc@buildgn
dynamic_cmd:cc:buildgn()
{
    DOCKER_ARGS+=(--build-only-gn)
    dynamic_cmd:cc:hosp
}

#help.cmd cc@alltest : (仓库) 编译所有测试
dynamic_cmd:cc:alltest()
{
    DOCKER_ARGS+=(--build-target make_all --build-target make_test)
    dynamic_cmd:cc:hosp
}

#help.cmd cc@sdk : (仓库) 编译sdk
#alias.cmd ocs : cc@sdk
dynamic_cmd:cc:sdk()
{
    PRODUCT=ohos-sdk
    dynamic_cmd:cc:hosp
}

PREPROCESS_ENABLE=false
# dynamic.option --preprocess : 生成预编译文件
# --preprocess.option.list: cc@rebuild
dynamic_option:--preprocess()
{
    PREPROCESS_ENABLE=true
}

ARGS_ENALE=false
# dynamic.option --args : 查看编译参数，而非重新编译
# --args.option.list: cc@rebuild
dynamic_option:--args()
{
    ARGS_ENALE=true
}

#help.cmd cc@rebuild [id[, id]*] : (仓库) 重新编译已经失败的文件，可以选择编译哪一项
#alias.cmd ocr : cc@rebuild
#alias.cmd ocra : cc@rebuild --args
#alias.cmd ocrp : cc@rebuild --preprocess
dynamic_cmd:cc:rebuild()
{
    if ! ohos:buildlog_init; then
        echo "没有需要重新编译的文件"
        return 0
    fi

    local ids=()
    local try_add_id=false

    arguments $*
    while arguments:have_next; do
        try_add_id=true
        local id="$(arguments:get_now)"
        if ! [[ "$id" =~ (${regex_dec_integer}) ]]; then
            output:logw "${id}不是十进制数字"
            continue
        elif ! (( "1" <= "$id" )); then
            output:logw "${id}小于1，是非法数字"
            continue
        elif ! (( "$id" <= "$(ohos:buildlog_count)" )); then
            output:logw "${id}大于错误的个数，是非法数字"
            continue
        else
            ids+=("$((id - 1))")
        fi

        arguments:next
    done
    arguments:destroy

    if $try_add_id && [ "${#ids[*]}" == "0" ]; then
        output:logi "没有可以重新编译的项"
        return 0
    fi

    if ! $try_add_id; then
        for (( i = 0; i < $(ohos:buildlog_count); i++ )); do
            ids+=($i)
        done
    fi

    for i in ${ids[*]}; do
        local command="$(ohos:buildlog_command $i)"
        if [ "$command" != "" ]; then
            if $PREPROCESS_ENABLE; then
                command+=" -E -o ../../$((i + 1)).cpp"
            fi

            if $ARGS_ENALE; then
                if which fzf >/dev/null; then
                    ohos:cd_repo
                    cd $OHOS_OUT_DIR
                    echo "$command" | sed 's@ @\n@g' | fzf --preview='p="{}"; [[ "$p" =~ (^.-I) ]] && ls -l "'$(pwd)'/${p:3:-1}"'
                else
                    echo "$command" | sed 's@ @\n@g' | less
                fi
                return 0
            fi

            DOCKER_CWD="$OHOS_OUT_DIR"
            local ccresult="$(docker:exec "$command" 2>&1)"
            if [ "$ccresult" != "" ]; then
                output:logi "重新编译($((i + 1))/$(ohos:buildlog_count)): $(cctl y)$command$(cctl 0)\n$(cctl g)编译结果：$(cctl 0)"
                echo "$ccresult"
            elif $PREPROCESS_ENABLE; then
                ohos:cd_repo || return
                filename="$(pwd)/$((i + 1)).cpp"
                cd $COMMAND_CURDIR
                output:logi "输出文件在$(print:pathfile:get_pathfile $filename)"
            fi
        fi
    done
}

# function dynamic_cmd:cc:rebuild:test()
# {
#     REPO_PATH="$(pwd)"
#     CONTAINER_NAME="ohos"
#     test_build_log=$OHOS_TOOL/snapshot/build_log_0
#     dynamic_cmd:cc:rebuild
# }
# test:register_test dynamic_cmd:cc:rebuild:test

#help.cmd cc@gencc : (仓库) 生成compile_commands.json在当前位置
#alias.cmd ocg : cc@gencc
dynamic_cmd:cc:gencc()
{
    ohos:cd_repo || return
    OHOS_DOCKER_USE=false docker:exec "ninja -C $OHOS_OUT_DIR -t compdb" > $COMMAND_CURDIR/compile_commands.json
}

#help.cmd cc@list : (仓库) 查看所有编译目标
#alias.cmd ocl : cc@list
dynamic_cmd:cc:list()
{
    ohos:cd_repo || return
    docker:exec gn ls $OHOS_OUT_DIR | tee gn_ls_result.txt
    filename="$(pwd)/gn_ls_result.txt"
    cd $COMMAND_CURDIR
    output:logi "输出文件在$(print:pathfile:get_pathfile $filename)"
}
